import { QueryFields } from '@dataui/crud-request';
import { objKeys, isTrue, isBoolean } from '@dataui/crud-util';
import { FindManyOptionsReq } from '@nest-crud/request-typeorm/find-options';
import { FindOptionsRelations, FindOptionsSelect } from 'typeorm';
import { JoinMapUtils } from '../utils/join-map.utils';

export function deepSetFieldsAndJoin(findOptions: FindManyOptionsReq) {
  const m = new JoinMapUtils();
  const fields: QueryFields = [];
  const relations = findOptions.relations as FindOptionsRelations<any>;

  //=== setJoin
  const deepSetJoin = (relations: FindOptionsRelations<any>, parents: string[] = []) => {
    objKeys(relations).forEach((relationTable) => {
      const relationOpt = relations[relationTable];
      if (!relationOpt) return; // false
      m.setJoin([...parents, relationTable]);
      if (!isBoolean(relationOpt)) {
        deepSetJoin(relationOpt, [...parents, relationTable]);
      }
    });
  };
  if (relations) deepSetJoin(relations);
  // === setRelationFields
  const deepSetRelationFields = (relationSelect: FindOptionsSelect<any>, fieldChain: string[]) => {
    const relationFields: string[] = [];
    objKeys(relationSelect).forEach((relationField) => {
      const item = relationSelect[relationField];
      if (isTrue(item)) {
        relationFields.push(relationField);
      } else if (!isBoolean(item)) {
        deepSetRelationFields(item, fieldChain.concat(relationField));
      }
    });
    m.setJoinSelect(fieldChain, relationFields);
  };
  //=== setFields
  const deepSetFields = (select: FindOptionsSelect<any>, parents: string[] = []) => {
    objKeys(select).forEach((field) => {
      const selectOpt = select[field];
      const fieldChain = [...parents, field];
      if (!selectOpt) return;
      if (isTrue(selectOpt)) fields.push(fieldChain.join('.'));
      else {
        if (m.isJoin(fieldChain)) {
          deepSetRelationFields(selectOpt, fieldChain);
        } else {
          deepSetFields(selectOpt, fieldChain);
        }
      }
    });
  };
  if (findOptions.select) deepSetFields(findOptions.select as FindOptionsSelect<any>);
  return { fields, join: m.getJoins() };
}
